#ifndef META_BASIC_IOSTREAM_HPP
#define META_BASIC_IOSTREAM_HPP

#include "..\..\exp_utilities\exp_utilities.hpp"
#include "..\..\meta_invoke\meta_invoke.hpp"
#include "..\..\meta_object\meta_objects.hpp"
#include "iostream_transfer_policy.hpp"

namespace meta_ios {
    namespace io_stream_transform_details {
        using exp_utilities::exp_list;
        using exp_utilities::to_exp_list_t;
        using meta_quote::unary;
        using meta_objects::meta_ret_object;
        using meta_quote::binary;
        using meta_objects::meta_object;
        using exp_utilities::get_type;


        namespace meta_basic_istream_detail {
            template<class this_list> using dec_f = typename this_list::pop_front;
            template<class this_list> using pop_f = typename this_list::front;
            
            template<class TL>
            using meta_basic_istream = meta_ret_object<to_exp_list_t<TL>, unary<dec_f>, unary<pop_f>>;
        }

        namespace meta_basic_ostream_detail {


            template<class none_list_type, class T>
            struct add_impl { using type = exp_utilities::literal_types::no_exist_type; };

            template<template<class...> class L, class T, class ...Tys>
            struct add_impl<L<Tys...>, T> {
                using type = L<Tys..., T>;
            };


            template<class this_list, class T>
            using add_f = typename add_impl<this_list, T>::type;

            template<class TL = exp_list<>>
            using meta_basic_ostream = meta_object<TL, binary<add_f>>;
        }

        namespace meta_transform_istream_detail {
            using meta_basic_istream_detail::dec_f;
            using meta_basic_istream_detail::pop_f;

            template<class meta_function_type, class this_list>
            using pop_transform_f = meta_invoke<meta_function_type, pop_f<this_list>>;

            template<class TL, class meta_function_type>
            using meta_basic_transform_istream = meta_ret_object<to_exp_list_t<TL>, unary<dec_f>, meta_quote::bind_binary<pop_transform_f, meta_function_type>>;
        }

        namespace meta_transform_ostream_detail {
            using meta_basic_ostream_detail::add_f;

            template<class meta_function_type>
            struct add_transform_f {
                template<class this_list, class from_ins>
                using apply = add_f<this_list, meta_invoke<meta_function_type, this_list, from_ins>>;
            };

            template<class TL, class meta_function_type>
            using meta_basic_transform_ostream = meta_object<TL, add_transform_f<meta_function_type>>;
        }

        namespace meta_join_ostream_detail {
            using meta_basic_ostream_detail::add_f;

            template<class T>
            struct add_to_this_list {
                template<class this_list>
                using apply = add_f<this_list, T>;
            };

            template<class this_list, class T>
            struct auto_join_f{
                using type = add_f<this_list, T>;
            };

            template<class this_list, template<class...>class L, class ...Tys>
            struct auto_join_f<this_list, L<Tys...>> {
                using type = meta_fold<this_list, add_to_this_list<Tys>::template apply...>;
            };

            template<class TL>
            using join_ostream = meta_object<TL, meta_quote::binary<auto_join_f>>;
        }

        namespace meta_filter_ostream_detail {
            using meta_basic_ostream_detail::add_f;

            template<class meta_function_type>
            struct add_filter_f {
                template<class this_list, class from_ins>
                using apply = std::conditional_t<
                    meta_invoke<meta_function_type, this_list, from_ins>::value,
                    add_f<this_list, from_ins>,
                    this_list
                >;
            };

            template<class TL, class filter>
            using filter_ostream = meta_object<TL, add_filter_f<filter>>;
        }

        namespace meta_index_istream_detail {
            using exp_utilities::Idx;
            using exp_utilities::inc_idx_t;
            using exp_utilities::to_meta_array_t;
            using exp_utilities::exp_select;
            template<std::size_t start> using my_counter = exp_list<Idx<start>>;

            template<class this_list>
            using make_inc_type = exp_list<inc_idx_t<exp_select<0, this_list>>>;

            template<class this_list>
            using ret_index = exp_select<0, this_list>;

            template<std::size_t start>
            using index_istream = meta_ret_object<my_counter<start>, meta_quote::unary<make_inc_type>, meta_quote::unary<ret_index>>;

        }

        namespace meta_replace_able_ostream_detail {
            using exp_utilities::literal_types::no_exist_type;
            template<class this_obj, class T>
            using replace_this = T;

            using replace_able_ostream = meta_object<no_exist_type, meta_quote::binary<replace_this>>;
        }

        namespace meta_self_repeat_ostream_detail {
            template<class this_obj>
            using ret_self = typename this_obj::template at<0>;

            template<class this_obj>
            using do_nothing = this_obj;

            template<class T>
            using self_ostream = meta_ret_object<exp_list<T>, meta_quote::unary<do_nothing>, meta_quote::unary<ret_self>>;
        }
    }
}

#endif